package org.apache.ibatis.common.type;

import org.apache.ibatis.common.io.Resources;

import java.sql.*;
import java.util.HashMap;
import java.util.Map;

/**
 * @Name UnkownTypeHandler
 * @Description: [SQL Unknown -> JAVA Object] 类型转换器
 * @Version v1.0.0
 * @Author: lichao
 * @Date: 2024-05-10 18:37:14
 **/
public class UnkownTypeHandler extends BaseTypeHandler<Object> {
    private static final ObjectTypeHandler OBJECT_TYPE_HANDLER = new ObjectTypeHandler();
    private TypeHandlerRegistry typeHandlerRegistry;

    public UnkownTypeHandler(TypeHandlerRegistry typeHandlerRegistry) {
        this.typeHandlerRegistry = typeHandlerRegistry;
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
        TypeHandler handler = resolveTypeHandler(parameter, jdbcType);
        handler.setParameter(ps, i, parameter, jdbcType);
    }

    @Override
    public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
        TypeHandler<?> handler = resolveTypeHandler(rs, columnName);
        return handler.getResult(rs, columnName);
    }

    @Override
    public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        TypeHandler<?> handler = resolveTypeHandler(rs.getMetaData(), columnIndex);
        if (handler == null || handler instanceof UnkownTypeHandler) {
            handler = OBJECT_TYPE_HANDLER;
        }
        return handler.getResult(rs, columnIndex);
    }

    @Override
    public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return cs.getObject(columnIndex);
    }

    private TypeHandler resolveTypeHandler(Object parameter, JdbcType jdbcType) {
        TypeHandler<?> handler;
        if (parameter == null) handler = OBJECT_TYPE_HANDLER;
        else {
            handler = typeHandlerRegistry.getTypeHandler(parameter.getClass(), jdbcType);
            if (handler == null || handler instanceof UnkownTypeHandler) {
                handler = OBJECT_TYPE_HANDLER;
            }
        }
        return handler;
    }

    private TypeHandler<?> resolveTypeHandler(ResultSet rs, String columnName) {
        try {
            Map<String, Integer> columnIndexLookup = new HashMap<>();
            ResultSetMetaData rsmd = rs.getMetaData();
            int count = rsmd.getColumnCount();
            for (int i = 1; i <= count; i++) {
                String name = rsmd.getColumnName(i);
                columnIndexLookup.put(name, i);
            }
            Integer columeIndex = columnIndexLookup.get(columnName);
            TypeHandler<?> handler = null;
            if (columeIndex != null) handler = resolveTypeHandler(rsmd, columeIndex);
            if (handler == null || handler instanceof UnkownTypeHandler) handler = OBJECT_TYPE_HANDLER;
            return handler;
        } catch (SQLException e) {
            String errorMsg = "无法确定 column [" + columnName + "] 的 JDBC 类型，错误原因为：" + e;
            throw new TypeException(errorMsg, e);
        }
    }

    private TypeHandler<?> resolveTypeHandler(ResultSetMetaData rsmd, int columnIndex) {
        TypeHandler<?> handler = null;
        JdbcType jdbcType = safeGetJdbcTypeForColumn(rsmd, columnIndex);
        Class<?> javaType = safeGetClassForColumn(rsmd, columnIndex);
        if (javaType != null && jdbcType != null) handler = typeHandlerRegistry.getTypeHandler(javaType, jdbcType);
        else if (javaType != null) handler = typeHandlerRegistry.getTypeHandler(javaType);
        else if (jdbcType != null) handler = typeHandlerRegistry.getTypeHandler(jdbcType);
        return handler;
    }

    private JdbcType safeGetJdbcTypeForColumn(ResultSetMetaData rsmd, int columnIndex) {
        try {
            return JdbcType.forCode(rsmd.getColumnType(columnIndex));
        } catch (Exception e) {
            return null;
        }
    }

    private Class<?> safeGetClassForColumn(ResultSetMetaData rsmd, int columnIndex) {
        try {
            return Resources.classForName(rsmd.getColumnClassName(columnIndex));
        } catch (Exception e) {
            return null;
        }
    }

}

